
#include "xmodule_config.h" 

#if MODULE_HEARTRATE_ENABLE && HX3300_ENABLE
#include <stdint.h>

#include "hx3300/hrs3300.h"


#define HX3300_SLAVE_ADDR       HX3300_SLAVE_ADDR_7BIT 
#define HX3300_TIMER_INTERVAL   40

const uint8_t  hrs3300_bp_timeout_grade = 0;  // max 15
const uint8_t  hrs3300_agc_init_stage = 0x04;  // init AGC state  
const uint8_t  hrs3300_bp_power_grade = 0;
const uint8_t  hrs3300_accurate_first_shot = 0;
const uint8_t  hrs3300_up_factor = 3;
const uint8_t  hrs3300_up_shift = 2;
const uint16_t hrs3300_AMP_LTH = 120;
const uint16_t hrs3300_hr_AMP_LTH = 150;
const uint16_t hrs3300_hr_PVAR_LTH = 10;

const uint8_t init_register_array[][2] = {
//{0x01, 0xd0}, //11010000  bit[7]=1,HRS enable;bit[6:4]=101,wait time=50ms,bit[3]=0,LED DRIVE=22 mA
//{0x01, 0xf0},   //11010000  bit[7]=1,HRS enable;bit[6:4]=101,wait time=50ms,bit[3]=0,LED DRIVE=22 mA v13.05
{0x0c, 0x4e},   //00001110  bit[6]=0,LED DRIVE=22mA;bit[5]=0,sleep mode;p_pulse=1110,duty=50% 
{0x16, 0x78},   //01111000  bits[7:4]=0111,HRS 15bits
{0x17, 0x0d},   //00001101  bits[7:5]=011,HRS gain 16*;bit[1]=0,HRS data=AD0 
{0x02, 0x00},
{0x03, 0x00},
{0x04, 0x00},
{0x05, 0x00},
{0x06, 0x00},
{0x07, 0x00},
{0x0b, 0x00},
{0x0c, 0x6e},
};
#define INIT_ARRAY_SIZE (sizeof(init_register_array)/sizeof(init_register_array[0]))


typedef struct{    
    uint16_t hrs;
    uint16_t als;
}hx3300_raw_data_t;

uint8_t start_get_hr=0;

// these variable is for algorithm 
uint8_t reg_0x7f ;
uint8_t reg_0x80 ;
uint8_t reg_0x81 ;
uint8_t reg_0x82 ;

static uint8_t m_local_cnt = 0; 


static bool __hx3300_read_byte(uint8_t reg, uint8_t *data)
{
    uint32_t ret = HR_I2C_REPEAT_READ( HX3300_SLAVE_ADDR, reg, data, 1 );
    if( ret != HR_I2C_SUCCESS )
    {
        LOG1("[HX3300]: read fail, ret = 0x%X\r\n", ret );
    }
    return ret == HR_I2C_SUCCESS;
}

static bool __hx3300_write_byte(uint8_t reg, uint8_t data)
{
    uint8_t w[2];
    hrs_op_data_t* write = (hrs_op_data_t*)w;
    write->reg = reg;
    write->data[0] = data;

    uint32_t ret = HR_I2C_WRITE( HX3300_SLAVE_ADDR, &write->reg, write->data, 1 );
    if( ret != HR_I2C_SUCCESS )
    {
        LOG1("[HX3300]: write fail, ret = 0x%X\r\n", ret );
    }
    
    return ret == HR_I2C_SUCCESS;
}

static int __hx3300_read_hrs(int16_t *hrs)
{
    uint8_t temp[3];
    
    __hx3300_read_byte( REG_C0DATAM_ADDR, &temp[0] );
    __hx3300_read_byte( REG_C0DATAH_ADDR, &temp[1] );
    __hx3300_read_byte( REG_C0DATAL_ADDR, &temp[2] );
    
    *hrs = (int16_t)( (temp[0] << 8) | ((temp[1] & 0x0F) << 4) | (temp[2] & 0x0F));
    
    return 0;
}

static int __hx3300_read_als(int16_t *als)
{
    uint8_t temp[3];
    
    __hx3300_read_byte( REG_C1DATAM_ADDR, &temp[0] );
    __hx3300_read_byte( REG_C1DATAH_ADDR, &temp[1] );
    __hx3300_read_byte( REG_C1DATAL_ADDR, &temp[2] );
    
    uint16_t data = (uint16_t)((temp[0] << 3) | ((temp[1] & 0x3F) << 11) | (temp[2] & 0x07) );
    if( data > 32767 )
    {
        *als = 32767;
    }
    else 
    {
        *als = 0;
    }
    return 0;
}

static void hx3300_enable(void)
{
    __hx3300_write_byte( 0x16, 0x78 );
    __hx3300_write_byte( 0x01, 0x00 );
    __hx3300_write_byte( 0x0c, 0x2E );
}
static void hx3300_disable(void)
{
    for( int i = 0; i < INIT_ARRAY_SIZE; i++)
    {
     __hx3300_write_byte( init_register_array[i][0], init_register_array[i][1] ); // _write_byte
    }	
    __hx3300_write_byte( 0x01, 0x08 );
    __hx3300_write_byte( 0x0c, 0x4E );
}



static
bool __hx3300_mounting(em_heartrate_desc_t* pdesc)
{
    bool ret = false;
    int i ;
    HRS_SET_SLAVE_ADDR( HX3300_SLAVE_ADDR );

    for( i = 0; i < 10 ; i ++ )
    {
        if( __hx3300_read_byte( REG_ID_ADDR, &pdesc->chip_id ) == false )
        {
            continue;
        }
        
        LOG1("[HX3300]: read chip id = 0x%X\r\n", pdesc->chip_id); 
        if( pdesc->chip_id == HS3300_DEV_ID )
        {
            ret = true;
            break;
        }
        
        HR_DELAY_MS( 10 );
    }
    
    return ret;
}


static
void __hx3300_setup( em_heartrate_init_t* p_init_obj)
{
    HRS_SET_SLAVE_ADDR( HX3300_SLAVE_ADDR );
    // init 
    __hx3300_read_byte( 0x7f, &reg_0x7f);
    __hx3300_read_byte( 0x80, &reg_0x80 );
    __hx3300_read_byte( 0x81, &reg_0x81 );
    __hx3300_read_byte( 0x82, &reg_0x82 );
    
    hr_timer_create( HX3300_TIMER_INTERVAL ); 
    
    // stop heartrate first 
    hr_timer_stop();    
    Hrs3300_alg_close();
    hx3300_disable();
}

static
void __hx3300_start(uint32_t mode)
{
    hr_work_mode = (em_hr_work_mode_t)mode;
    // hr_work_mode = EMHR_WORK_MODE_HEARTRATE;
    HRS_SET_SLAVE_ADDR( HX3300_SLAVE_ADDR );

    hx3300_enable();
    Hrs3300_alg_open();
    
    // timer start 
    hr_timer_start();
    hrs_set_dlps();
   // LOG("[EMHR]: start");
   start_get_hr=0;
  // LOG1("[EMHR]: start,mode=%d.",mode);	 
}

static
void __hx3300_stop(void)
{
    LOG("[EMHR]: stop");
    // timer stop 
    hr_timer_stop();

    HRS_SET_SLAVE_ADDR( HX3300_SLAVE_ADDR );
    Hrs3300_alg_close();
    hx3300_disable();
    hrs_reset_dlps();
}

// ALGO LIB port function 
#if 1 //
bool Hrs3300_write_reg( uint8_t addr, uint8_t data ) 
{
    return __hx3300_write_byte( addr, data ) == HR_I2C_SUCCESS;
}
uint8_t Hrs3300_read_reg( uint8_t addr )
{
    uint8_t data; 
    __hx3300_read_byte( addr, &data) ;
    return data;
}
void Hrs3300_chip_enable(void)
{
    // __hx3300_start();
    hx3300_enable();
    Hrs3300_alg_open();
    
    // timer start 
    hr_timer_start();
}
void Hrs3300_chip_disable(void)
{
    // __hx3300_stop();
    hr_timer_stop();
    // start_get_hr = EMHR_WORK_MODE_HEARTRATE;
    Hrs3300_alg_close();
    hx3300_disable();
}
#endif 

static void get_data_type(em_hrs_evt_type_t *datatype)
{
    if( hr_work_mode == EMHR_WORK_MODE_HEARTRATE )
    {
        *datatype = EM_HRS_EVT_TYPE_HEARTRATE_RESULT;
    }
    else if( hr_work_mode == EMHR_WORK_MODE_BLOODPRESSURE )
    {
        *datatype = EM_HRS_EVT_TYPE_BP_RESULT;
    }
    else if( hr_work_mode == EMHR_WORK_MODE_DETECT)
    {
        *datatype = EM_HRS_EVT_TYPE_CROSSTALK;
    }
    else 
    {
        *datatype = EM_HRS_EVT_TYPE_INVALID;
    }    
}


// static
// void __hx3300_timeout(void)
// {

// }

static
void __hx3300_process( void )
{
    int16_t hrs, als;
    static uint8_t bloodpressure_cnt=0;
	uint8_t bpm = 60;
		
    HRS_SET_SLAVE_ADDR( HX3300_SLAVE_ADDR );

    __hx3300_read_hrs( &hrs );
    __hx3300_read_als( &als );
    
    Hrs3300_alg_send_data( hrs, als, 0, 0, 0, 0 );
    
    m_local_cnt ++;
    
    if( m_local_cnt >= 25 ) 
    {
        LOG("[EMHR]: timeout");
        m_local_cnt = 0;
        
        hrs3300_results_t result = Hrs3300_alg_get_results();
        
        if( m_hr_local_cb == NULL )
        {
            return ;
        }
        em_heartrate_evt_t evt;
        get_data_type( &evt.type );
        APP_PRINT_INFO4("[HX3300]: hrs = %d, als = %d, status = %d,hr_work_mode=%d\r\n", hrs, als, result.alg_status,hr_work_mode );

        if( hr_work_mode == EMHR_WORK_MODE_DETECT )
        {
            evt.type = EM_HRS_EVT_TYPE_CROSSTALK;
            evt.param.CROSSTALK.code = (em_crosstalk_code_t)0;
            evt.param.CROSSTALK.value[0] = hrs;
            evt.param.CROSSTALK.value[1] = als;
            m_hr_local_cb( &evt );
        }

       if((!start_get_hr)&&(hr_work_mode==2))
       	{
       	  hr_work_mode = EMHR_WORK_MODE_HEARTRATE;
       	}
	   else if((result.hr_result != 0)&&(!start_get_hr))
	   {
		 LOG1("[EMHR]: hr=%d.",result.hr_result);	 
		 start_get_hr = 1;
		 hr_work_mode = EMHR_WORK_MODE_HEARTRATE;
		 }
				
        switch( result.alg_status ) 
        {
            case MSG_ALG_NOT_OPEN: // internal 
                break;
            
            case MSG_NO_TOUCH:     
            {                
                switch( hr_work_mode )
                {
                    case EMHR_WORK_MODE_HEARTRATE:	
                    evt.param.HR.type = EM_HR_EVT_TYPE_NO_TOUCH;
                    break;
                    case EMHR_WORK_MODE_BLOODPRESSURE:
                    evt.param.BP.type = EM_BP_EVT_TYPE_NO_TOUCH;
                    break;
					//case EMHR_WORK_MODE_DETECT:
					//evt.param.HR.type = EM_HR_EVT_TYPE_NO_TOUCH;
					//__hx3300_stop();	
					//break;

                }                
                m_hr_local_cb( &evt );
            }
                break;
            
            case MSG_PPG_LEN_TOO_SHORT:
                break;
            
            case MSG_HR_READY:
            {
                switch( hr_work_mode )
                {
                    case EMHR_WORK_MODE_HEARTRATE:
                    evt.param.HR.type = EM_HR_EVT_TYPE_READY;
                    evt.param.HR.BPM = result.hr_result;
                    m_hr_local_cb( &evt );
					hr_work_mode = EMHR_WORK_MODE_BLOODPRESSURE;
                    break;

                    case EMHR_WORK_MODE_BLOODPRESSURE:
                    {  
                        hrs3300_bp_results_t bp = Hrs3300_alg_get_bp_results();
                        evt.param.BP.type = EM_BP_EVT_TYPE_READY;

				
						
                        if( bp.sbp != 0 && bp.dbp != 0 )
                        {  //LOG2("[EMHR]: MODE_BLOODPRESSURE,BP=%d,%d.",bp.sbp,bp.dbp);
                            evt.param.BP.SBP = bp.sbp;
                            evt.param.BP.DBP = bp.dbp;
                            m_hr_local_cb( &evt );
							
                        }
						bloodpressure_cnt++;
						if((bloodpressure_cnt>2)&&( bp.sbp != 0 && bp.dbp != 0 ))
						{
						  bloodpressure_cnt=0;
						__hx3300_stop();
						}
                    }
                    break;
					//case EMHR_WORK_MODE_DETECT:
					//	LOG0("1126  EMHR_WORK_MODE_DETECT");	
					//	__hx3300_stop();
                    //break;
                    default:
					//	evt.param.HR.type = EM_HR_EVT_TYPE_NO_TOUCH;
					//	__hx3300_stop();
					//	LOG1("1124  hr_work_mode=%d",hr_work_mode);	
                    break;
                }
            }
                break;
            
            case MSG_ALG_TIMEOUT:
                break;
            
            case MSG_SETTLE:
                break;
        }
    }
}


const em_heartrate_instance_t HR_HX3300 = 
{    
    .mounting = __hx3300_mounting, 
    .setup = __hx3300_setup,
    // .set_work_mode = em_heartrate_set_work_mode,
    
    .start = __hx3300_start,
    .stop = __hx3300_stop,
    
    .register_callback = em_heartrate_register_callback,
    
    .timeout = __hx3300_process, 
    .process = __hx3300_process, 
};

#endif 
